package com.gin.kotlin.gf2gamecommunity.base.livedata

import android.util.Log
import androidx.lifecycle.MutableLiveData
import com.gin.kotlin.gf2gamecommunity.app.App
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.awaitResponse

/**
 * 可缓存LiveData封装
 * @param T 响应对象类型
 * @param D 需要保存的数据类型
 * @param requestMethod 请求方法
 * @param bodyTransform 响应对象到保存数据的转换方法
 * @param defaultExpire 缓存默认的过期时长
 * @constructor
 */
class CachedLiveData<T, D>(
    private val requestMethod: CachedLiveData<T, D>.() -> Call<T>,
    private val bodyTransform: CachedLiveData<T, D>.(T) -> D?,
    private val defaultExpire: Long = 300L,
) {
    val liveData = MutableLiveData<D>()
    val loading = MutableLiveData(false)

    private var lastSuccess = 0L

    /**
     * 获取数据
     * @param force 是否强制刷新
     * @param expire 缓存过期时长（秒）
     */
    fun fetch(force: Boolean = false, expire: Long = defaultExpire, onSuccess: (data: D) -> Unit = {}): MutableLiveData<D> {
        // 如果当前正在请求 则 跳过
        if (loading.value == true) return liveData
        // 如果非强制请求， 且数据未过期 则 跳过
        if (!force && System.currentTimeMillis() / 1000 - lastSuccess <= expire) return liveData
        // 开始请求
        loading.value = true
        requestMethod().enqueue(object : Callback<T> {
            override fun onResponse(call: Call<T>, response: Response<T>) {
                loading.value = false
                val code = response.code()

                if (code == 200) {
                    // 请求成功更新数据和上次成功时间
                    liveData.value = response.body()?.let { bodyTransform(it)?.also { b -> onSuccess(b) } }
                    lastSuccess = System.currentTimeMillis() / 1000
                } else {
                    // 请求失败
                    App.toast("$code: ${response.message()}")
                }
            }

            /**
             * 网络故障等导致的请求失败
             * @param call Call<T>
             * @param throwable Throwable
             */
            override fun onFailure(call: Call<T>, throwable: Throwable) {
                loading.value = false
                App.onFailed(throwable)
            }
        })
        return liveData
    }

    suspend fun fetchAwait(force: Boolean = false, expire: Long = 300L): D? {
        // 如果当前正在请求 则 跳过
        if (loading.value == true) throw RuntimeException("当前正在执行请求")
        // 如果非强制请求， 且数据未过期 则 跳过
        if (!force && System.currentTimeMillis() / 1000 - lastSuccess <= expire) return liveData.value
        loading.value = true
        try {
            val response = requestMethod().awaitResponse()
            val code = response.code()
            if (code == 200) {
                lastSuccess = System.currentTimeMillis() / 1000
                return response.body()?.let { bodyTransform(it) }.also { liveData.value = it }
            }
            Log.w(TAG, "请求响应为空")
            return null
        } catch (e: Exception) {
            throw e
        } finally {
            loading.value = false
        }
    }


    fun clear() {
        liveData.value = null
    }

    companion object {
        private val TAG = CachedLiveData::class.java.simpleName
    }
}